<?php
/**
 *  MyApp_ManagerCache.php
 *
 *  @package
 *  @version
 */

/**
 *  MyApp_ManagerCache
 *
 *  @author
 *  @access     public
 *  @package    Smscom
 */
define('MANAGER_CACHE_TIME_SHORT'      ,  60 * 10);
define('MANAGER_CACHE_TIME_MIDDLE'      , 60 * 60);
define('MANAGER_CACHE_TIME_LONG'        , 60 * 60 * 24);
define('MANAGER_CACHE_TIME_SUPER_LONG'  , 60 * 60 * 24 * 7);

class MyApp_ManagerCache{

    const CACHE_NAMESPACE = "cycache";

    private static $cm = null;
    private $obj= null;

    var $allowed_cache_method_list = array(
    );
    var $_force_clear = false;

    var $_through_cache = false;


    function __construct($obj = null,&$backend = null){
        if(!is_null($obj)){
            $this->obj = $obj;
        }
        if(!is_null($backend)){
            $this->backend =& $backend;
        }elseif(!is_null($obj)){
            $this->backend =& $this->_getObj()->backend;
        }
    }

    /**
     * _getCm
     * ----------------------------------
     * return :Cachemanager
     */
    protected function &_getCm(){
        if(is_null(self::$cm)){
            self::$cm =& $this->backend->plugin->getPlugin('Cachemanager', 'Memcache');
            self::$cm->_getMemcache(0,self::CACHE_NAMESPACE);
        }
        return self::$cm;
    }

    /**
     * _getObj
     * ----------------------------------
     * return :Cachemanager
     */
    protected function _getObj(){
        return $this->obj;
    }
    
    /**
     * _getObj
     * ----------------------------------
     * return :Cachemanager
     */
    protected function _getObjName(){
        return get_class($this->obj);
    }
    
    /**
     * mkCacheKey
     * ----------------------------------
     * param @method_name:string cache
     * param @method_args:array(int=>mixed)
     * return :string
     */
    protected function mkCacheKey($method_name,$method_args){
        $ret = $this->mkCacheKeyByMethod($method_name);
        if(count($method_args) != 0){
            $ret .= "_" . implode( "_",$method_args );
        }
        return $ret;
    }

    /**
     * mkCacheKeyByMethod
     * ----------------------------------
     * param @key_prefix:string cache
     * param @method_args:array(int=>mixed)
     * return :string
     */
    protected function mkCacheKeyByMethod($method_name){
        return $this->_getObjName() . "." . $method_name;
    }

    /**
     * isCache
     * ----------------------------------
     * param @key:string  cache
     * param @lifetime:int
     * return :bool true false
     */
    protected function isCache($key,$lifetime){
        return (bool)($this->_getCm()->isCached($key, $lifetime, self::getNamespace() ));
    }

    /**
     * getCache
     * ----------------------------------
     * param @key:string  cache
     * param @lifetime:int
     * return :mixed cache 
     */
    protected function getCache($key,$lifetime){
        return unserialize($this->_getCm()->get($key, $lifetime, self::getNamespace() ) );
    }

    /**
     * setCache
     * ----------------------------------
     * param @key:string  cache
     * param @val:mixed cache
     * return :bool true: false:
     */
    protected function setCache($key,$val){
        return $this->_getCm()->set($key, serialize($val),null, self::getNamespace() );
    }

    /**
     * getNamespace
     * ----------------------------------
     * return :string
     */
    protected static function getNamespace(){
        return self::CACHE_NAMESPACE;
    }

    /**
     * getLifetime
     * ----------------------------------
     * param @methodName:string
     * return :int
     */
    protected function getLifetime($methodName){
        return $this->allowed_cache_method_list[$methodName];
    }

    /**
     * useCache
     * -------------------------------------------------------------------------------------------------------------
     * param @methodName:string
     * param @args:array(int=>mixed)
     * return :bool [true -> | false -> true ->]
     */
    protected function useCache($methodName, $args){
        if($this->getThroughCache()){
            return false;
        }
        if(!in_array($methodName, array_keys($this->allowed_cache_method_list) ) ){
            return false;
        }
        foreach($args as $val){
            if(is_array($val)){
                return false;
            }
        }
        return true;
    }

    /**
     * cache_call
     * ---------------------------------------------------------
     * param @key:string  cache
     * param @methodName:string
     * param @args:array(int=>mixed)
     * return :mixed
     */
    protected function cache_call($key,$methodName, &$args){
        if(!$this->getForceClear() && $this->isCache($key,$this->getLifetime($methodName) ) ){
            return $this->getCache($key,$this->getLifetime($methodName));
        }
        $ret = call_user_func_array(array($this->_getObj(),$methodName),$args);
        $this->setCache($key,$ret);
        return $ret;
    }
    
    /**
     * _getKeysByMethodName
     * param @_method_name:string
     * param @_namespace:string
     * ----------------------------------
     * return :array(string)
     */
    protected function _getKeysByMethodName($_class_name,$_method_name,$_namespace){
        $ret = array();
        $keys = $this->_getCm()->getKeys($_namespace);
        foreach($keys as $key){
            list($class_name,$method_clause) = explode(".",$key,2);
            if($class_name != $_class_name || $method_clause == ''){
                continue;
            }
            
            list($method_name,$params) = explode("_",$method_clause,2);
            if($method_name != $_method_name){
                continue;
            }
            $ret[] = $key;
        }
        return $ret;
    }

    /**
     * _getKeysByClassName
     * param @_method_name:string
     * param @_namespace:string
     * ----------------------------------
     * return :array(string)
     */
    protected function _getKeysByClassName($_class_name,$_namespace){
        $ret = array();
        $keys = $this->_getCm()->getKeys($_namespace);
        foreach($keys as $key){
            list($class_name,$method_clause) = explode(".",$key,2);
            if($class_name != $_class_name || $method_clause == ''){
                continue;
            }
            $ret[] = $key;
        }
        return $ret;
    }

    /**
     * __call  this.obj
     * ------------------------------------------------------------------------
     * param @methodName:string
     * param @args:array(int=>mixed)
     * return :mixed
     */
    function __call($methodName, $args){
        if(!$this->useCache($methodName, $args)){
            return call_user_func_array(array($this->_getObj(),$methodName),$args);
        }
        $CACHE_KEY = $this->mkCacheKey($methodName,$args);
        return $this->cache_call($CACHE_KEY,$methodName, $args);
    }

    /**
     * clearAll
     * ----------------------------------
     * return :bool true: false:
     */
    public function clearAll(){
        $keys = $this->_getCm()->getKeys(self::getNamespace());
         try{
            foreach($keys as $key){
                $this->_getCm()->clear($key, self::getNamespace());
            }
        }catch(Exception $e){
            return false;
        }
        return true;
    }

    /**
     * clearClass
     * ----------------------------------
     * return :boolか true false
     */
    public function clearClass($class_name){
        $keys = $this->_getKeysByClassName($class_name, self::getNamespace());
        try{
            foreach($keys as $key){
                $this->_getCm()->clear($key, self::getNamespace());
            }
        }catch(Exception $e){
            return false;
        }
        return true;
    }

    /**
     * clearMethod method_name
     * param @method_name:string
     * ----------------------------------
     * return :bool true:false:
     */
    function clearMethod( $class_name,$method_name){
        $keys = $this->_getKeysByMethodName($class_name ,$method_name, self::getNamespace());
        try{
            foreach($keys as $key){
                $this->_getCm()->clear($key, self::getNamespace());
            }
        }catch(Exception $e){
            return false;
        }
        return true;
    }

    /**
     * 
     **/
    public function setForceClear($force_clear){
        $this->_force_clear = (bool)$force_clear;
    }
    public function getForceClear(){
        return $this->_force_clear;
    }
    public function setThroughCache($through_cache){
        $this->_through_cache = (bool)$through_cache;
    }
    public function getThroughCache(){
        return $this->_through_cache;
    }
    
}
?>
